package com.itheima.d8_generics_method;

import java.util.ArrayList;


/**
 * 泛型方法:
 * 修饰符 <类型变量，类型变量，…>  返回值类型 方法名(形参列表) {
 *
 *  }
 * public static <T> void test(T t){
 *
 * }
 *
 * 限定T能接的数据类型:所以把<T>改成<T extends Car>，这样就限定了T能接的类型一定是car或者car的子类
 *  通配符:
 *  就是 “?” ，可以在“使用泛型”的时候代表一切类型；  E T K V 是在定义泛型的时候使用。(看本案例方法go3的解释)
 *
 * 泛型的上下限：
 *  泛型上限:   ? extends Car:   ? 能接收的必须是Car或者其子类 。
 *  泛型下限：  ?  super Car ： ?  能接收的必须是Car或者其父类。
 *
 *  泛型限定的意思是对泛型的数据类型进行范围的限制。有如下的三种格式
 *
 * - <?> 表示任意类型
 * - <? extends 数据类型> 表示指定类型或者指定类型的子类
 * - <? super 数据类型> 表示指定类型或者指定类型的父类
 */

/**
 * 泛型的擦除问题和注意事项:
 * 泛型是工作在编译阶段的，一旦程序编译成class文件，class文件中就不存在泛型了，这就是泛型擦除。
 * 泛型不支持基本数据类型，只能支持对象类型（引用数据类型）。
 */
public class Test {
    public static void main(String[] args) {
        // 目标：掌握泛型方法的定义和使用。
        String rs = test("java");   //按住ctrl键，鼠标移到test方法上面不要点击，可以看到test方法的返回类型
        System.out.println(rs);
        System.out.println(rs.getClass());
        System.out.println("==============");
        int rs1 = test(25);
        System.out.println(rs1);

        Dog d = test(new Dog());
        System.out.println(d);

        // 需求：所有的汽车可以一起参加比赛。
        ArrayList<Car> cars = new ArrayList<>();
        cars.add(new BMW());
        cars.add(new BENZ());
        go(cars);

        ArrayList<BMW> bmws = new ArrayList<>();
        bmws.add(new BMW());
        bmws.add(new BMW());
        //调用静态方法go方法报错，因为public static void go(ArrayList<Car> cars)。
        // go方法里的形参接收的是(ArrayList<Car>)car类型的集合。而 bmws不是car类型（虽然是继承了car类型）
        //go(bmws);  报错


       /* 如果把泛型方法go写成public static <T> void go(ArrayList<T> cars)这种形式，
            那么对接收的数据类型又会是缺少约束，比如dog类型也能动作车类型接受进来，这不符合业务需求*/
        ArrayList<Dog> dogs = new ArrayList<>();
        dogs.add(new Dog());
        dogs.add(new Dog());
        go1(dogs);    //定义的泛型方法不符要求



        ArrayList<BENZ> benzs = new ArrayList<>();
        benzs.add(new BENZ());
        benzs.add(new BENZ());
        go2(benzs);
        //go2(dogs);  报错
        go2(bmws);
        go2(cars);

        go3(benzs);

    }

   /*
     由go2简化而来
     1. ? 通配符，在使用泛型的时候可以代表一切类型（而E，T什么的是我们自己定义的类型变量）
     2. 此时arraylist本身就是存在的泛型,是别人定义好的泛型。所以我们是在使用这个泛型，那么此时就可以用？来代表一切类型
     3. 并且为了限制可以接收的数据类型，写成：? extends Car(上限)，意思就是这个“？”最多可以接收car类，下限可以接收car的子类
     ? super Car(下限) 这个意思就是“？”只能是car或者是car的父类
     */
    public static void go3(ArrayList<? extends Car> cars){

    }

    //限定T能接的数据类型:所以把<T>改成<T extends Car>，这样就限定了T能接的类型一定是car或者car的子类
    //但是<T extends Car>这里单独定义了一个泛型，ArrayList<T> 这个本身就是一个泛型类。所以是没有必要写两个泛型的
    //为了简化，可以把go2改成go3的模式
    public static <T extends Car> void go2(ArrayList<T> cars){

    }

    //对接收的数据类型又会是缺少约束，比如dog类型也能动作车类型接受进来，这不符合业务需求，所有要限定T能接的数据类型
    public static <T> void go1(ArrayList<T> cars){

    }
    public static void go(ArrayList<Car> cars){

    }

    // 泛型方法
    //这里用static是因为需要在main方法中进行调用，而main方法是static方法，也只能调用static方法
    public static <T> T test(T t){
        return t;
    }
}
